package nz.ac.massey.groupproject.ecs2;

import javax.swing.JFrame;
import javax.swing.JPanel;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import java.awt.Dimension;
import java.awt.geom.AffineTransform;

import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;


public class GUI {
	private static JFrame            window;
	private static Canvas            canvas;
	private static GUIEventHandler   events;

	// The following runs in main thread

	static void setupGUI()   throws InterruptedException, InvocationTargetException {
		// For thread safety, do GUI stuffs in Swing's thread. https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/swing/package-summary.html#threading
		javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
			public void run() {
				canvas = new Canvas();
				setupWindow();
				events = new GUIEventHandler(window);
			}
		});
	}

	static void scheduleAtInterval(int delay,  ActionListener callback) {
		new javax.swing.Timer (delay, callback).start();
	}

	// The following runs in Swing's thread

	static void drawFrame(BufferedImage img) {
		canvas.imageForNextFrame = img;
		canvas.repaint();
	}

	static Dimension getCanvasSize() {
		return canvas.getSize();
	}

	static boolean isKeyDown(int keycode)  throws IndexOutOfBoundsException /* just a reminder */ {
		return events.keyDown[keycode];
	}

	// TODO center window.setLocationRelativeTo(null);
	// TODO prevent resize
	private static void setupWindow() {

		// create invisible window
		window = new JFrame("Demo Game");
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		window.add(canvas);

		var sz = new Dimension(800, 600);    // TODO Adjust according to game world

		canvas.setPreferredSize(sz);         // setSize() and setMinimumSize() don't work
		window.pack();

		// There is probably some hideous hack behind the scene.
		// getPreferredSize() only seems to be correct if we never called it before setVisible()
		// However, setMinimumSize(getPreferredSize()) appears to work regardless of when we call setVisible()
		window.setVisible(true);

		sz = window.getSize();
		var w = Integer.valueOf(sz.width).toString(); // + Integer.valueOf(sz.width).toString()

		//System.err.println(w+w);
		var ww = w+w;          // NEED HELP:  if var ww = w, window size will be incorrect
		//sz = window.getPreferredSize();         System.err.println("win pref height " + sz.height + "     w = " + sz.width);
		var winsz = window.getPreferredSize();
		//sz = window.getPreferredSize();         System.err.println("win pref height " + sz.height + "     w = " + sz.width);
		window.setMinimumSize(winsz);
	}
}

// Always run in Swing's thread
class Canvas extends JPanel {

	BufferedImage   imageForNextFrame   = null;

	@Override
	protected void paintComponent(Graphics gg) {
		super.paintComponent(gg);
		if (null == imageForNextFrame) return;

		var g = (Graphics2D) gg;
		g.drawImage(imageForNextFrame, /* no transform */ new AffineTransform(1,0,0,1,0,0), null);
	}

	// flush graphics buffer on Linux
	//   Even if we don't force a sync(), paintComponent() will still be called in a timely manner.
	//   However, the resulting buffer will not be displayed in a timely manner.
	@Override
	public void repaint() {
		super.repaint();
		java.awt.Toolkit.getDefaultToolkit().sync();
	}
}

class GUIEventHandler extends KeyAdapter {

	boolean keyDown[];

	GUIEventHandler(JFrame window) {
		keyDown = new boolean[530];
		for (int i = 0; i < keyDown.length; ++i)
			keyDown[i] = false;

		window.setFocusable(true);                   // just in case
		window.setFocusTraversalKeysEnabled(false);  // don't hijack Tab
		window.requestFocusInWindow();               // just in case
		window.addKeyListener(this);
	}

	// It is possible that multiple keyPressed fires, then a single keyReleased fires.
	@Override
	public void keyPressed(KeyEvent e) {
		int code = e.getKeyCode();
		if (code >= keyDown.length) return;
		keyDown[code] = true;

		//System.err.println(code + " pressed " + (char) code);
	}

	@Override
	public void keyReleased(KeyEvent e) {
		int code = e.getKeyCode();
		if (code >= keyDown.length) return;
		keyDown[code] = false;

		//System.err.println(e.getKeyCode() + " released");
	}
}

